home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / C / Applications / Python 1.3 / source code / Misc / pyimenu.el < prev    next >
Encoding:
Text File  |  1995-12-17  |  11.6 KB  |  306 lines  |  [TEXT/R*ch]

  1. ;;; PYIMENU.EL --- 
  2.  
  3. ;; Copyright (C) 1995 Perry A. Stoll
  4.  
  5. ;; Author: Perry A. Stoll <stoll@atr-sw.atr.co.jp>
  6. ;; Created: 12 May 1995
  7. ;; Version: 1.0
  8. ;; Keywords: tools python imenu
  9.  
  10. ;; This program is free software; you can redistribute it and/or modify
  11. ;; it under the terms of the GNU General Public License as published by
  12. ;; the Free Software Foundation; either version 2, or (at your option)
  13. ;; any later version.
  14.  
  15. ;; This program is distributed in the hope that it will be useful,
  16. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18. ;; GNU General Public License for more details.
  19.  
  20. ;; A copy of the GNU General Public License can be obtained from the
  21. ;; Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
  22. ;; USA.
  23.  
  24. ;;;; COMMENTS
  25.  
  26. ;; I use the imenu package a lot for looking at Lisp and C/C++
  27. ;; code. When I started using Python, I was dismayed that I couldn't
  28. ;; use it to look at Python source. So here's a rough go at it.
  29.  
  30. ;;;; USAGE
  31.  
  32. ;; This program is used in conjunction with the imenu package. When
  33. ;; you call imenu in python-mode in a python buffer, a list of
  34. ;; functions and classes is built. The top level menu has a list of
  35. ;; all functions and classes defined at the top indentation
  36. ;; level. Classes which have methods defined in them have a submenu
  37. ;; which contains all definitions contained in them. Selecting any
  38. ;; item will bring you to that point in the file.
  39.  
  40. ;;;; INSTALLATION
  41.  
  42. ;; You know the routine:
  43. ;; 1) Save this file as pyimenu.el,
  44. ;; 2) Place that file somewhere in your emacs load path (maybe ~/emacs
  45. ;;    or ~/emacs/lisp),
  46. ;; 3) Byte compile it (M-x byte-compile-file),
  47. ;; 4) Add the following (between "cut here" and "end here") to your
  48. ;;    ~/.emacs file,
  49. ;; 5) Reboot. (joke: DON'T do that, although you'll probably have to
  50. ;;    either reload your ~/.emacs file or start a new emacs)
  51.  
  52. ;;--------------cut here-------------------------------------------
  53. ;;;; Load the pyimenu index function
  54. ;;(autoload 'imenu "imenu" nil t)
  55. ;;(autoload 'imenu-example--create-python-index "pyimenu")
  56. ;;;; Add the index creation function to the python-mode-hook 
  57. ;;(add-hook 'python-mode-hook
  58. ;;      (function
  59. ;;       (lambda ()
  60. ;;         (setq imenu-create-index-function
  61. ;;           (function imenu-example--create-python-index)))))
  62. ;;----------------end here--------------------------------------------
  63. ;;
  64. ;; That is all you need. Of course, the following provides a more
  65. ;; useful interface. i.e. this is how I have it set up ;-)
  66. ;;
  67. ;;----------------optionally cut here----------------------------------
  68. ;;(autoload 'imenu-add-to-menubar "imenu" nil t)
  69. ;;(defun my-imenu-install-hook ()
  70. ;;  (imenu-add-to-menubar (format "%s-%s" "IM" mode-name)))
  71. ;;(add-hook 'python-mode-hook (function my-imenu-install-hook))
  72. ;;;; Bind imenu to some convenient (?) mouse key. This really lets you
  73. ;;;; fly around the buffer. Here it is set to Meta-Shift-Mouse3Click.
  74. ;;(global-set-key [M-S-down-mouse-3] (function imenu))
  75. ;;-----------------optionaly end here-----------------------------------
  76.  
  77. ;;;; CAVEATS/NOTES
  78.  
  79. ;; 0) I'm not a professional elisp programmer and it shows in the code
  80. ;;    below. If anyone there has suggestions/changes, I'd love to
  81. ;;    hear them. I've tried the code out on a bunch of python files
  82. ;;    from the python-1.1.1 Demo distribution and it worked with
  83. ;;    them - your mileage may very.
  84. ;;
  85. ;; 1) You must have the imenu package to use this file. This file
  86. ;;    works with imenu version 1.11 (the version included with emacs
  87. ;;    19.28) and imenu version 1.14; if you have a later version, this
  88. ;;    may not work with it.
  89. ;;
  90. ;; 2) This setup assumes you also have python-mode.el, so that it can
  91. ;;    use the python-mode-hook. It comes with the python distribution.
  92. ;;
  93. ;; 3) I don't have the Python 1.2 distribution yet, so again, this may
  94. ;;    not work with that.
  95. ;;
  96.  
  97. (require 'imenu)
  98.  
  99. ;;;
  100. ;;; VARIABLES: customizable in your .emacs file.
  101. ;;;
  102.  
  103. (defvar imenu-example--python-show-method-args-p nil 
  104.   "*When using imenu package with python-mode, whether the arguments of
  105. the function/methods should be printed in the imenu buffer in addition
  106. to the function/method name. If non-nil, args are printed.")
  107.  
  108. ;;;
  109. ;;; VARIABLES: internal use.
  110. ;;;
  111. (defvar imenu-example--python-class-regexp
  112.   (concat                              ; <<classes>>
  113.    "\\("                               ;
  114.    "^[ \t]*"                           ; newline and maybe whitespace
  115.    "\\(class[ \t]+[a-zA-Z0-9_]+\\)"    ; class name
  116.                                        ; possibly multiple superclasses
  117.    "\\([ \t]*\\((\\([a-zA-Z0-9_, \t\n]\\)*)\\)?\\)"
  118.    "[ \t]*:"                           ; and the final :
  119.    "\\)"                               ; >>classes<<
  120.    )
  121.   "Regexp for Python classes for use with the imenu package."
  122. )
  123.  
  124. (defvar imenu-example--python-method-regexp
  125.   (concat                               ; <<methods and functions>>
  126.    "\\("                                ; 
  127.    "^[ \t]*"                            ; new line and maybe whitespace
  128.    "\\(def[ \t]+"                       ; function definitions start with def
  129.    "\\([a-zA-Z0-9_]+\\)"                ;   name is here
  130.                     ;   function arguments...
  131.    "[ \t]*(\\([a-zA-Z0-9_=,\* \t\n]*\\))"
  132.    "\\)"                                ; end of def
  133.    "[ \t]*:"                            ; and then the :
  134.    "\\)"                                ; >>methods and functions<<
  135.    )
  136.   "Regexp for Python methods/functions for use with the imenu package."
  137.   )
  138.  
  139. (defvar imenu-example--python-method-no-arg-parens '(2 8)
  140.   "Indicies into the parenthesis list of the regular expression for
  141. python for use with imenu. Using these values will result in smaller
  142. imenu lists, as arguments to functions are not listed.
  143.  
  144. See the variable imenu-example--python-show-method-args-p to for
  145. information")
  146.  
  147. (defvar imenu-example--python-method-arg-parens '(2 7)
  148.   "Indicies into the parenthesis list of the regular expression for
  149. python for use with imenu. Using these values will result in large
  150. imenu lists, as arguments to functions are listed.
  151.  
  152. See the variable imenu-example--python-show-method-args-p to for
  153. information")
  154.  
  155. ;; Note that in this format, this variable can still be used with the
  156. ;; imenu--generic-function. Otherwise, there is no real reason to have
  157. ;; it.
  158. (defvar imenu-example--generic-python-expression
  159.   (cons
  160.    (concat 
  161.     imenu-example--python-class-regexp
  162.     "\\|"  ; or...
  163.     imenu-example--python-method-regexp
  164.     )
  165.    imenu-example--python-method-no-arg-parens)
  166.   "Generic Python expression which may be used directly with imenu by
  167. setting the variable imenu-generic-expression to this value. Also, see
  168. the function \\[imenu-example--create-python-index] for an alternate
  169. way of finding the index.")
  170.  
  171. ;; These next two variables are used when searching for the python
  172. ;; class/definitions. Just saving some time in accessing the
  173. ;; generic-python-expression, really.
  174. (defvar imenu-example--python-generic-regexp)
  175. (defvar imenu-example--python-generic-parens)
  176.  
  177. ;;;
  178. ;;; CODE:
  179. ;;;
  180.  
  181. ;; Note:
  182. ;; At first, I tried using some of the functions supplied by
  183. ;; python-mode to navigate through functions and classes, but after a
  184. ;; while, I decided dump it. This file is relatively self contained
  185. ;; and I liked it that.
  186.  
  187. ;;;###autoload
  188. (defun imenu-example--create-python-index ()
  189.   "Interface function for imenu package to find all python classes and
  190. functions/methods. Calls function
  191. \\[imenu-example--create-python-index-engine]. See that function for
  192. the details of how this works."
  193.   (setq imenu-example--python-generic-regexp
  194.     (car imenu-example--generic-python-expression))
  195.   (setq imenu-example--python-generic-parens
  196.     (if imenu-example--python-show-method-args-p
  197.         imenu-example--python-method-arg-parens
  198.       imenu-example--python-method-no-arg-parens))
  199.   (goto-char (point-min))
  200.   (imenu-example--create-python-index-engine nil))
  201.  
  202. (defun imenu-example--create-python-index-engine (&optional start-indent)
  203. "Function for finding all definitions (classes, methods, or functions)
  204. in a python file for the imenu package. 
  205.  
  206. Retuns a possibly nested alist of the form \(INDEX-NAME
  207.  INDEX-POSITION). The second element of the alist may be an alist,
  208. producing a nested list as in \(INDEX-NAME . INDEX-ALIST).
  209.  
  210. This function should not be called directly, as it calls itself
  211. recursively and requires some setup. Rather this is the engine for the
  212. function \\[imenu-example--create-python-index].
  213.  
  214. It works recursively by looking for all definitions at the current
  215. indention level. When it finds one, it adds it to the alist. If it
  216. finds a definition at a greater indentation level, it removes the
  217. previous definition from the alist. In it's place it adds all
  218. definitions found at the next indentation level. When it finds a
  219. definition that is less indented then the current level, it retuns the
  220. alist it has created thus far.
  221.  
  222.  The optional argument START-INDENT indicates the starting indentation
  223. at which to continue looking for python classes, methods, or
  224. functions. If this is not supplied, the function uses the indentation
  225. of the first definition found. "
  226.   (let ((index-alist '())
  227.     (sub-method-alist '())
  228.     looking-p
  229.     def-name prev-name
  230.     cur-indent def-pos
  231.     (class-paren (first  imenu-example--python-generic-parens)) 
  232.     (def-paren   (second imenu-example--python-generic-parens)))
  233.     (setq looking-p
  234.       (re-search-forward imenu-example--python-generic-regexp
  235.                  (point-max) t))
  236.     (while looking-p
  237.       (save-excursion
  238.     ;; used to set def-name to this value but generic-extract-name is
  239.     ;; new to imenu-1.14. this way it still works with imenu-1.11
  240.     ;;(imenu--generic-extract-name imenu-example--python-generic-parens))
  241.     (let ((cur-paren (if (match-beginning class-paren)
  242.                  class-paren def-paren)))
  243.       (setq def-name
  244.         (buffer-substring (match-beginning cur-paren)
  245.                   (match-end  cur-paren))))
  246.     (beginning-of-line)
  247.     (setq cur-indent (current-indentation)))
  248.  
  249.       ;; HACK: want to go to the correct definition location. Assuming
  250.       ;; here that there are only two..which is true for python.
  251.       (setq def-pos
  252.         (or  (match-beginning class-paren)
  253.          (match-beginning def-paren)))
  254.  
  255.       ; if we don't have a starting indent level, take this one
  256.       (or start-indent
  257.       (setq start-indent cur-indent))
  258.  
  259.       ; if we don't have class name yet, take this one
  260.       (or prev-name
  261.       (setq prev-name def-name))
  262.  
  263.       ;; what level is the next definition on? 
  264.       ;; must be same, deeper or shallower indentation
  265.       (cond
  266.  
  267.        ;; at the same indent level, add it to the list...
  268.        ((= start-indent cur-indent)
  269.     (push (cons def-name def-pos) index-alist))
  270.  
  271.        ;; deeper indented expression, recur...
  272.        ((< start-indent cur-indent)
  273.  
  274.     ;; the point is currently on the expression we're supposed to
  275.     ;; start on, so go back to the last expression. The recursive
  276.     ;; call will find this place again and add it to the correct
  277.     ;; list
  278.     (re-search-backward imenu-example--python-generic-regexp
  279.                 (point-min) 'move)
  280.     (setq sub-method-alist (imenu-example--create-python-index-engine
  281.                 cur-indent))
  282.  
  283.     (if sub-method-alist
  284.         ;; we put the last element on the index-alist on the start
  285.         ;; of the submethod alist so the user can still get to it.
  286.         (let ((save-elmt (pop index-alist)))
  287.           (push (cons (imenu-create-submenu-name prev-name)
  288.               (cons save-elmt sub-method-alist))
  289.             index-alist))))
  290.  
  291.        ;; found less indented expression, we're done.
  292.        (t 
  293.     (setq looking-p nil)
  294.     (re-search-backward imenu-example--python-generic-regexp 
  295.                 (point-min) t)))
  296.       (setq prev-name def-name)
  297.       (and looking-p
  298.        (setq looking-p
  299.          (re-search-forward imenu-example--python-generic-regexp
  300.                     (point-max) 'move))))
  301.     (nreverse index-alist)))
  302.  
  303. (provide 'pyimenu)
  304.  
  305. ;;; PyImenu.EL ends here
  306.